#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#ifndef TRUE
    #define TRUE    1 
#endif

#ifndef FALSE
    #define FALSE   0 
#endif

#define MAX_FILENAME_SIZE           2048       // Maximum filename length

#define SOURCE_FILE_EXT             ".pmp"     // Extension of a source code file
#define EXEC_FILE_EXT               ".pmpx"    // Extension of an executable code file

#define MAX_SOURCE_CODE_SIZE        65536      // Maximum number of lines in source code
#define MAX_SOURCE_LINE_SIZE        4096       // Maximum source line length
                                                
#define XSE_ID_STRING               "PIMP-ALPHA-0_1"      // Written to the file to state it's validity
#define VERSION_MAJOR               0          // Major version number
#define VERSION_MINOR               1          // Minor version number

#define MAX_LEXEME_SIZE             256        // Maximum lexeme size

#define LEX_STATE_NO_STRING         0          // Lexemes are scanned as normal
#define LEX_STATE_IN_STRING         1          // Lexemes are scanned as strings
#define LEX_STATE_END_STRING        2          // Lexemes are scanned as normal, and the next state is LEXEME_STATE_NOSTRING 

#define TOKEN_TYPE_INT              0          // An integer literal
#define TOKEN_TYPE_FLOAT            1          // An floating-point literal
#define TOKEN_TYPE_STRING           2          // An string literal
#define TOKEN_TYPE_QUOTE            3          // A double-quote
#define TOKEN_TYPE_IDENT            4          // An identifier
#define TOKEN_TYPE_COLON            5          // A colon
#define TOKEN_TYPE_OPEN_BRACKET     6          // An openening bracket
#define TOKEN_TYPE_CLOSE_BRACKET    7          // An closing bracket
#define TOKEN_TYPE_COMMA            8          // A comma
#define TOKEN_TYPE_OPEN_BRACE       9          // An openening curly brace
#define TOKEN_TYPE_CLOSE_BRACE      10         // An closing curly brace
#define TOKEN_TYPE_NEWLINE          11         // A newline

#define TOKEN_TYPE_INSTR            12         // An instruction
                                               //
#define TOKEN_TYPE_SETSTACKSIZE     13         // The SetStackSize directive
#define TOKEN_TYPE_SETPRIORITY      14         // The SetPriority directive
#define TOKEN_TYPE_VAR              15         // The Var/Var [] directives
#define TOKEN_TYPE_FUNC             16         // The Func directives
#define TOKEN_TYPE_PARAM            17         // The Param directives
#define TOKEN_TYPE_REG_RETVAL       18         // The _RetVal directives

#define TOKEN_TYPE_INVALID          19         // Error code for invalid tokens
#define END_OF_TOKEN_STREAM         20         // The end of the stream has been reached

#define MAX_IDENT_SIZE              256        // Maximum identifier size

/*
 * Data Structure for Symbol Tables
 */
struct symbol { /* a word */
    char *name;
    struct ref *reflist;
};

struct ref {
    struct ref *next;
    char *filename;
    int flags;
    int lineno;
};

/*
 * Lexer
 */

typedef int Token;                              // Tokenizer alias type

typedef struct _Lexer                           // The lexical analyzer/tokenizer
{
    int iCurrSourceLine;                        // Current line in the source
    unsigned int iIndex0,                       // Indices into the string
    iIndex1;

    Token CurrToken;                            // Current token
    char pstrCurrLexeme [ MAX_LEXEME_SIZE ];    // Current lexeme
    int iCurrLexState;                          // The current lex state
} Lexer;

void lex_reset();
Token lex_nexttok();
char* lex_curlexeme();
char lex_lookaheadchar ();
int lex_nextline ();

Lexer g_Lexer;                                  // The lexer
// ---- Source Code -----------------------------------------------------------------------
char ** g_ppstrSourceCode = NULL;               // Pointer to dynamically allocated
// array of string pointers.
int g_iSourceCodeSize;                          // Number of source lines

FILE * g_pSourceFile = NULL;                    // Source code file pointer

char g_pstrSourceFilename [ MAX_FILENAME_SIZE ],    // Source code filename
g_pstrExecFilename [ MAX_FILENAME_SIZE ];  // Executable filename

void ResetLexer ()
{
// Set the current line to the start of the file
//
g_Lexer.iCurrSourceLine = 0;
//
// Set both indices to point to the start of the string

g_Lexer.iIndex0 = 0;
g_Lexer.iIndex1 = 0;
// Set the token type to invalid, since a token hasn't been read yet

g_Lexer.CurrToken = TOKEN_TYPE_INVALID;

// Set the lexing state to no strings

g_Lexer.iCurrLexState = LEX_STATE_NO_STRING;
}

/* simple symtab of fixed size */
#define NHASH 9997
struct symbol symtab[NHASH];
struct symbol* symbol_lookup(char*);
void 	symbol_addref(int, char*, char*,int);
char* 	curfilename; /* name of current input file */

/* hash a symbol */
static unsigned symhash(char *sym)
{
    unsigned int hash = 0;
    unsigned c;
    while(c = *sym++) 
        hash = hash*9 ^ c;
    return hash;
}

struct symbol* symbol_lookup(char* sym)
{
    struct symbol *sp = &symtab[symhash(sym)%NHASH];
    int scount = NHASH; /* how many have we looked at */
    while(--scount >= 0) {
        if(sp->name && !strcmp(sp->name, sym)) return sp;
        if(!sp->name) { /* new entry */
            sp->name = strdup(sym);
            sp->reflist = 0;
            return sp;
        }
        if(++sp >= symtab+NHASH) sp = symtab; /* try the next entry */
    }
    fputs("symbol table overflow\n", stderr);
    abort(); /* tried them all, table is full */
}

void symbol_addref(int lineno, char *filename, char *word, int flags)
{
    struct ref *r;
    struct symbol *sp = symbol_lookup(word);
    /* don't do dups of same line and file */
    if(sp->reflist &&
            sp->reflist->lineno == lineno &&
            sp->reflist->filename == filename) return;
    r = malloc(sizeof(struct ref));
    if(!r) {fputs("out of space\n", stderr); abort(); }
    r->next = sp->reflist;
    r->filename = filename;
    r->lineno = lineno;
    r->flags = flags;
    sp->reflist = r;
}

/* print the references
 * * sort the table alphabetically
 * * then flip each entry's reflist to get it into forward order
 * * and print it out
 * */
/* aux function for sorting */
static int symcompare(const void *xa, const void *xb)
{
    const struct symbol *a = xa;
    const struct symbol *b = xb;
    if(!a->name) {
        if(!b->name) return 0; /* both empty */
        return 1; /* put empties at the end */
    }
    if(!b->name) return -1;
    return strcmp(a->name, b->name);
}


void printrefs()
{
    struct symbol *sp;
    qsort(symtab, NHASH, sizeof(struct symbol), symcompare); /* sort the symbol table */
    for(sp = symtab; sp->name && sp < symtab+NHASH; sp++) {
        char *prevfn = NULL; /* last printed filename, to skip dups */
        /* reverse the list of references */
        struct ref *rp = sp->reflist;
        struct ref *rpp = 0; /* previous ref */
        struct ref *rpn; /* next ref */
        do {
            rpn = rp->next;
            rp->next = rpp;
            rpp = rp;
            rp = rpn;
        } while(rp);
        /* now print the word and its references */
        printf("%10s", sp->name);
        for(rp = rpp; rp; rp = rp->next) {
            if(rp->filename == prevfn) {
                printf(" %d", rp->lineno);
            } else {
                printf(" %s:%d", rp->filename, rp->lineno);
                prevfn = rp->filename;
            }
        }
        printf("\n");
    }
}

/*
 * String Processing Prototypes
 */
int char_iswspace(char c);
int char_isnum(char c);
int char_isident(char c);
int char_isdelim(char c);
void trim_wspace(char *s);
int string_iswspace(char *s);
int string_isident(char *s);
int string_isint(char *s);
int string_isfloat(char *s);
void strip_comments(char* s);

/*
 * exit and Error Functions
 */
void exit ();
void exit_err(char* errmsg);
void exit_code_err(char* errmsg);
void exit_char_expected_err(char c);


int main(int argc, char **argv)
{

    printf("Hello World!\n");
    return 0;
}

void strip_comments(char * s)
{
    unsigned int i;
    int instring;

    instring = 0;

    for (i = 0; i < strlen(s) - 1; ++i)
    {
        if (s[i] == '"' ) {
            if (instring)
                instring = 0;
            else
                instring = 1;
        }

        if (s[i] == ';')
        {
            if (! instring)
            {
                s[i] = '\n';
                s[i + 1] = '\0';
                break;
            }
        }
    }
}

int string_isfloat(char* s)
{
    unsigned int i;
    int hasradix = 0;

    if (! s)
        return FALSE;

    if (strlen(s) == 0)
        return FALSE;

    for (i = 0; i < strlen(s); ++i)
        if (! char_isnum(s[i]) && ! (s[i] == '.') && ! (s[i] == '-'))
            return FALSE;

    for (i = 0; i < strlen(s); ++i) {
        if (s[i] == '.') {
            if (hasradix)
                return FALSE;
            else
                hasradix = 1;
        }
    }

    for (i = 1; i < strlen(s); ++i)
        if (s[i] == '-')
            return FALSE;

    if (hasradix)
        return TRUE;
    else
        return FALSE;
}

int string_isint(char* s)
{
    unsigned int i;

    if (! s)
        return FALSE;

    if (strlen(s) == 0)
        return FALSE;

    for (i = 0; i < strlen(s); ++i)
        if (! char_isnum(s[i]) && ! (s[i] == '-'))
            return FALSE;

    for (i = 1; i < strlen(s); ++i)
        if (s[i] == '-')
            return FALSE;

    return TRUE;
}

int string_isident(char* s)
{
    unsigned int i;

    if (! s)
        return FALSE;

    if (strlen(s) == 0)
        return FALSE;

    if (s[0] >= '0' && s[0] <= '9')
        return FALSE;

    for (i = 0; i < strlen(s); ++i)
        if (! char_isident(s[i]))
            return FALSE;

    return TRUE;
}

int string_iswspace(char * s)
{
    unsigned int i;

    if (! s)
        return FALSE;

    if (strlen(s) == 0)
        return TRUE;

    for (i = 0; i < strlen(s); ++i)
        if (! char_iswspace(s[i]) && s[i] != '\n')
            return FALSE;

    return TRUE;
}

void trim_wspace(char* s)
{
    unsigned int len = strlen(s);
    unsigned int padlen;
    unsigned int i;

    if (len > 1)
    {
        for (i = 0; i < len; ++i)
            if (! char_iswspace(s[i]))
                break;

        padlen = i;
    
        if (padlen)
        {
            for (i = padlen; i < len; ++i)
                s[i-padlen] = s[i];
                for (i = len - padlen; i < len; ++i)
                    s[i] = ' ';
        }

        for (i = len - 1; i > 0; --i)
        {
            if (! char_iswspace(s[i]))
            {
                s[i+1] = '\0';
                break;
            }
        }
    }
}

int char_isdelim(char c)
{
    if (c == ':' || c == ',' || c == '"' ||
        c == '[' || c == ']' ||
        c == '{' || c == '}' ||
        char_iswspace(c) || c == '\n')
        return TRUE;
    else
        return FALSE;
}

int char_isident(char c)
{
    if ((c >= '0' && c <= '9') ||
        (c >= 'A' && c <= 'Z') ||
        (c >= 'a' && c <= 'z') ||
        c == '_')
        return TRUE;
    else
        return FALSE;
}

int char_isnum(char c)
{
    if (c >= '0' && c <= '9')
        return TRUE;
    else
        return FALSE;
}

int char_iswspace(char c)
{
    if (c == ' ' || c == '\t')
        return TRUE;
    else
        return FALSE;
}

